package org.bsplus.dao.jpa;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.bsplus.dao.IssueDAO;
import org.bsplus.domain.Issue;
import org.bsplus.domain.IssueSeverity;
import org.bsplus.domain.IssueType;
import org.bsplus.domain.Project;
import org.bsplus.util.pagination.PageLoadRequest;
import org.bsplus.util.pagination.ResultPage;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.util.Assert;

public class JpaIssueDAO extends JpaDAO implements IssueDAO
{
    @Override
    public void saveOrUpdateIssue(final Issue issue)
    {
        Assert.notNull(issue, "Issue must not be null");
        Assert.notNull(issue.getProject(), "Issue must have a project");

        // a bit of messing around to ensure the last issue number is
        // incremeneted
        // and assignedcorrectly

        Project project = issue.getProject();
        project = getJpaTemplate().find(Project.class, project.getId());
        issue.setProject(project);
        getJpaTemplate().execute(new JpaCallback<Void>()
        {
            @Override
            public Void doInJpa(EntityManager em) throws PersistenceException
            {
                em.lock(issue.getProject(), LockModeType.WRITE);
                return null;
            }
        });

        // refresh after our exclusive lock
        getJpaTemplate().refresh(issue.getProject());

        long issueNumber = issue.getProject().getLastIssueNumber();
        issueNumber++;

        issue.getProject().setLastIssueNumber(issueNumber);
        issue.setIssueNumber(issueNumber);

        getJpaTemplate().persist(issue.getProject());
        getJpaTemplate().persist(issue);
    }

    @Override
    public List<Issue> getAllIssues()
    {
        return findAll(Issue.class);
    }

    @Override
    public List<IssueType> getAllIssueTypes()
    {
        return findAll(IssueType.class);
    }

    @Override
    public List<IssueSeverity> getAllIssueSeverities()
    {
        return findAll(IssueSeverity.class);
    }

    @Override
    public ResultPage<Issue> getIssues(final PageLoadRequest pageLoadRequest)
    {
        String fromString = "from " + Issue.class.getName() + " i ";
        String queryString = fromString;


        final String resultQuery = queryString;

        final String countQuery = "select count (i.id) " + fromString;

        @SuppressWarnings("unchecked")
        List<Issue> results = getJpaTemplate().executeFind(new JpaCallback<List<Issue>>()
        {
            @Override
            @SuppressWarnings("unchecked")
            public List<Issue> doInJpa(EntityManager em) throws PersistenceException
            {

                Query query = em.createQuery(resultQuery);
                query.setFirstResult(pageLoadRequest.getOffset());
                query.setMaxResults(pageLoadRequest.getLimit());
                return query.getResultList();
            }
        });

        Long resultCount = getJpaTemplate().execute(new JpaCallback<Long>()
        {
            @Override
            public Long doInJpa(EntityManager em) throws PersistenceException
            {
                Query query = em.createQuery(countQuery);
                return (Long)query.getSingleResult();
            }
        });

        return new ResultPage<Issue>(results, resultCount.intValue());
    }
}
